package jp.sourceforge.akjrcp.util;

import com.akjava.lib.standards.process.ProcessMonitor;
import com.akjava.lib.standards.process.ProcessPrinter;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.sourceforge.akjrcp.commons.util.FileNameUtils;
import jp.sourceforge.akjrcp.ui.DirectryBasedExecuterListener;
import jp.sourceforge.akjrcp.ui.URLBasedExecuterListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


//TODO
//support fmt mode

public class YoutubeDLExecuter {
	public static Log log = LogFactory.getLog(YoutubeDLExecuter.class);

private URLBasedExecuterListener executeListener;

private String exportDir;
private File exportFile;
private Process process;
private boolean cancelling;

private File currentOutputFile;
private boolean renameFile;

private boolean insertMeta; //set meta.

private String youtubeDLPath;

private String pythonPath;

private int waitsecond=1000*15;
private static Pattern progressPattern=Pattern.compile("^Retrieving video data:\\s*(.*)%.*");
private static Pattern progressPattern2=Pattern.compile("^\\[download\\]\\s*(.*)%.*");
private int result;

private String user;
private String password;



private int format=18;
public boolean isCancelling() {
	return cancelling;
}

public void setCancelling(boolean cancelling) {
	this.cancelling = cancelling;
}

public boolean isRenameFile() {
	return renameFile;
}

public void setRenameFile(boolean renameFile) {
	this.renameFile = renameFile;
}



public String getYoutubeDLPath() {
	return youtubeDLPath;
}

public void setYoutubeDLPath(String youtubeDLPath) {
	this.youtubeDLPath = youtubeDLPath;
}

public void execute(URL urls[]){
	if(executeListener!=null){
		executeListener.beforeConvert();
	}
	
	for (int i = 0; i < urls.length; i++) {
		execute(urls[i]);
		if(i!=urls.length-1){
			try {
				Thread.sleep(waitsecond);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	if(executeListener!=null){
		executeListener.afterConvert();
	}
}

private void execute(URL url){
	if(cancelling){
		log.info("cancel "+url);
		return;
	}
	inExecute(url);
}

public void cancel(){
	log.info("called cancel");
	synchronized(this){
		log.info("cancell:"+System.currentTimeMillis());
		cancelling=true;
		}
	if(process!=null){
		process.destroy();
		log.info("maybe destroyed:"+process);
	}
	
	if(currentOutputFile!=null){
		currentOutputFile.delete();
	}
	
}

public static String getExtension(int format){
	String ext="mp4";
	if(format==0)
		ext="flv";
	if(format==13)
		ext="3gp";
	if(format==17)
		ext="3gp";
	if(format==5)
		ext="flv";
	if(format==6)
		ext="flv";
	if(format==34)
		ext="flv";
	return ext;
}
public String[] toCommand(URL url){
	List<String> list=new ArrayList<String>();
	if(pythonPath!=null){
		list.add(pythonPath);
	}
	list.add(youtubeDLPath);
	String key=YoutubeUtils.getId(url);
	if(key==null){
		log.info("key is null:"+url);
		return null;
	}
	if(exportDir==null){
		log.info("export dir=null:"+url);
	}
	String extension=getExtension(format);
	if(format!=0){
		list.add("-f");
		list.add(""+format);
	}
	//user and password
	if(user!=null && password!=null){
		list.add("-u");
		list.add(user);
		list.add("-p");
		list.add(password);
	}
	
	list.add("-o");
	if(exportFile!=null){
		list.add(exportFile.getAbsolutePath());
		currentOutputFile=exportFile;
	}else{
	if(renameFile){
		String title=YoutubeUtils.getTitle(url);
		if(title.equals("Broadcast Yourself")){
			title=key;
		}
		String path=exportDir+"\\"+YoutubeUtils.fixFileName(title)+"."+extension;
		currentOutputFile=new File(path);
		list.add(path);//basename;
	}else{
		//usekey
		String path=exportDir+"\\"+key+"."+extension;
		currentOutputFile=new File(path);
		list.add(path);//basename;
	}
	}
	String wwwUrl=YoutubeUtils.toWWWUrl(url.toString());
	log.info("wwwurl:"+wwwUrl);
	list.add(wwwUrl);
	
	return (String[]) list.toArray(new String[list.size()]);
}

public static String toDebugArray(String[] array,String password){
	String v="";
	for (int i = 0; i < array.length; i++) {
		if(password!=null && !password.equals("") && array[i].equals(password)){
			
			v+="\""+"*****"+"\""+" ";
		}else{
			v+="\""+array[i]+"\""+" ";
			
		}
	}
	return v+"\n";
}
private void inExecute(URL url){
	if(cancelling){
		log.info("cancel "+url);
		return;
	}
	

	try {
	
		
			
		String[] commands=toCommand(url);
		if(commands==null){
			log.warn("null command");
			return;
		}
		
		if(executeListener!=null){
			executeListener.startConvert(url);
		}
		
		Runtime runtime=Runtime.getRuntime();

		ProcessPrinter printer=new ErrorCatchProcessPrinter();
		try {
			printer.printOutput(toDebugArray(commands,password));
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		log.info("start process:"+process+","+toDebugArray(commands,password));
		
		
		
		
		process = runtime.exec(commands);
		
		//log.info("start process:"+process+","+ArrayUtils.toString(commands));
		log.info("process:"+System.currentTimeMillis()+","+cancelling);
		
		ProcessMonitor monitor=new ProcessMonitor(process,printer);
		monitor.startMonitor();
		
		try {
			if(!cancelling){
			process.waitFor();
			
			result=process.exitValue();
			if(result!=0){
			if(result==1){
				log.info("exit-value:"+result);
				
				
			}else{
				printer.printError("[error] "+result+"\n");
				
				if(result==-1073741819){
					log.warn("exit-value:"+result+"maybe memory error");
					
				}else{
					log.warn("exit-value:"+result);
					//unknown memory
					
					}
				
				//anyway warn and log
				//Thread.sleep(500);
				}
			}else{
				//rename when extension wrong.
				
				
					File newOutput=new File(FileNameUtils.getChangedExtensionName(currentOutputFile, getExtension(format),true));
					boolean ret=currentOutputFile.renameTo(newOutput);
					if(!ret){
						log.warn("rename to "+getExtension(format)+" faild:"+currentOutputFile);
					}else{
						log.info("rename to " +getExtension(format)+":"+currentOutputFile);
					}
				
			}
			}else{
			log.info("finally cancel "+url);
			return;
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	} catch (IOException e1) {
		//maybe catch not supported codecs.
		e1.printStackTrace();
		process.destroy();
		if(executeListener!=null){
			executeListener.faildConvert(url,e1.getMessage());
			}
		return;
	}finally{

		
	}
	
	//call finish
	if(executeListener!=null){
		if(result==0){
			executeListener.endConvert(url);
		}else{
			
			executeListener.faildConvert(url, ""+result);
			}
		}
	
	process=null;
	//cancelling=false; damm.this time cancell all only.
}

public class TestMonitor implements DirectryBasedExecuterListener{

	public void beforeConvert() {
		// TODO Auto-generated method stub
		
	}

	public void afterConvert() {
		// TODO Auto-generated method stub
		
	}

	public void startConvert(File file) {
		// TODO Auto-generated method stub
		
	}

	public void endConvert(File file) {
		// TODO Auto-generated method stub
		
	}

	public void faildConvert(File file, String message) {
		log.info("faild:"+file);
	}

	public void printProcess(String line) {
		// TODO Auto-generated method stub
		
	}

	public boolean askOverwrite(File output) {
		// TODO Auto-generated method stub
		return false;
	}
	
}



public URLBasedExecuterListener getExecuteListener() {
	return executeListener;
}

public void setExecuteListener(URLBasedExecuterListener executeListener) {
	this.executeListener = executeListener;
}

public String getExportDir() {
	return exportDir;
}

public void setExportDir(String exportDir) {
	this.exportDir = exportDir;
}




public class ErrorCatchProcessPrinter implements ProcessPrinter{
	public void printOutput(String output) throws IOException {
		print(output);
	}

	public void printError(String error)  throws IOException{
		print(error);
	}

	private void print(String line) throws IOException{
		if(executeListener!=null){
			executeListener.printProcess(line);
			}
		log.debug(line);
		
		if(YoutubeDLUtils.isFLVWarningLine(line)){
			//flvDownloaded=true;
		}else if(YoutubeDLUtils.isMP4WarningLine(line)){
			//flvDownloaded=false;
		}
		/*
		 * catch something.
		if(line.indexOf(DirectryBasedExecuterListener.ERROR_NOTSUPPORTED_VIDEO)!=-1){
			//cancel();
			throw new IOException(DirectryBasedExecuterListener.ERROR_NOTSUPPORTED_VIDEO);
		}
		*/
		
	}
	
}

public File getCurrentOutputFile() {
	return currentOutputFile;
}
public void setCurrentOutputFile(File currentOutputFile) {
	this.currentOutputFile = currentOutputFile;
}

public String getPythonPath() {
	return pythonPath;
}

public void setPythonPath(String pythonPath) {
	this.pythonPath = pythonPath;
}

public static boolean isProgressLine(String line){
	return line.startsWith("Retrieving") || line.startsWith("[download]");
}

public static int getProgressPersent(String line){
	Matcher matcher=progressPattern.matcher(line.replaceAll("\n", ""));
	if(matcher.matches()){
		int value=(int) Double.parseDouble(matcher.group(1));
		return value;
	}else{
		Matcher matcher2=progressPattern2.matcher(line.replaceAll("\n", ""));
		if(matcher2.matches()){
			int value=(int) Double.parseDouble(matcher2.group(1));
			return value;
		}else{
			log.info("not mutch");
		}
	}
	
	return 0;
}

public int getResult() {
	return result;
}

public void setResult(int result) {
	this.result = result;
}

public File getExportFile() {
	return exportFile;
}

public void setExportFile(File exportFile) {
	this.exportFile = exportFile;
}


public String getUser() {
	return user;
}

public void setUser(String user) {
	this.user = user;
}

public String getPassword() {
	return password;
}

public void setPassword(String password) {
	this.password = password;
}



public int getFormat() {
	return format;
}

public void setFormat(int format) {
	this.format = format;
}
}
